<?php

namespace addons\csmding\controller;

use think\addons\Controller;
use addons\csmding\library\Csmding;
use addons\csmding\library\dingtalk\DingTalkClient;
use addons\csmding\library\dingtalk\DingTalkConstant;
use addons\csmding\library\dingtalk\OapiDepartmentListRequest;
use addons\csmding\library\dingtalk\OapiDepartmentGetRequest;
use addons\csmding\library\dingtalk\OapiUserListbypageRequest;
use fast\Random;
use addons\csmding\library\dingtalk\OapiUserGetRequest;
use addons\csmding\library\dingtalk\OapiAuthScopesRequest;
use app\admin\library\Auth;

class Cli extends Controller
{
    
    private $maxQueryDepartUserCount = 100;
    
    protected function _initialize()
    {
        parent::_initialize(); // TODO: Change the autogenerated stub
        //钉钉日志目录
        if (!defined("TOP_SDK_WORK_DIR")) {
            define("TOP_SDK_WORK_DIR", "../runtime/temp/");
        }
    }

    /**
     * 断言是否允许访问
     * 只有在cli模式或者是admin账号下可以访问
     *
     * @return void
     */
    public function _assertAllowIndex(){
        if($this->request->isCli()){
            return;
        }
        $auth = Auth::instance();
        $currentadminuser = $auth->getUserInfo();
        if($currentadminuser!=null && $currentadminuser['username']=='admin'){
            return ;
        }
        $this->error('只有admin账号或者在终端进行操作!');
    }

    /**
     * http://127.0.0.1/fastadmin_plugin_csmmeet/public/addons/csmding/cli/index
     * http://fatest.163fan.com/addons/csmding/cli/index
     * 1.钉钉部门同步到本地 clidepart
     * > 判定是否同步开通部门(由于需要把管理员id回写到dd表中)
     * > 创建或更新钉钉部门
     * > 钉钉员工同步到本地 clideuser
     * > 判定是否同步开通员工
     * > 创建或更新钉钉员工
     * 3.不是本地标记的员工删除 cleardduser
     * 4.不是本地标记的部门删除 cleardddepart
     * 5.同步dd部门到csmadmin部门 syncCsmDepart
     * 6.同步dd员工到faadmin syncFaAdmin
     * 7.同步dd任职到csmadmin任职
     */
    public function index()
    {
        $this->_assertAllowIndex();

        set_time_limit(0);
        ignore_user_abort(true); // 设置断开连接继续执行
        header('X-Accel-Buffering: no'); // 关闭buffer
        header('Content-type: text/html;charset=utf-8'); // 设置网页编码
        header("Connection:Keep-Alive"); // http 1.0 持久化连接
        header("Proxy-Connection:Keep-Alive"); // http 1.1 持久化连接
        ob_start(); // 打开输出缓冲控制
        static::println('执行开始');
        $start_int_time = time();
        $access_token = null;
        try {
            $access_token = Csmding::getAccessToken();
        } catch (\Exception $e) {
            $this->error($e->getMessage());
            exit();
        }
        $remoteversion = time(); // 版本号
        $c = new DingTalkClient(DingTalkConstant::$CALL_TYPE_OAPI, DingTalkConstant::$METHOD_GET, DingTalkConstant::$FORMAT_JSON);
        $scoperesp = $this->cliauthScope($c, $access_token);
        if (isset($scoperesp->errcode) && $scoperesp->errcode != 0) {
            // throw new \Exception($scoperesp->errmsg);
            $this->error('执行错误:' . $scoperesp->errcode . ' ' . $scoperesp->errmsg);
        }
        if (isset($scoperesp->auth_org_scopes)) {
            // 根据授权部门同步
            if (isset($scoperesp->auth_org_scopes->authed_dept)) {
                if (! in_array(1, $scoperesp->auth_org_scopes->authed_dept)) {
                    $this->_asynrootdepart($remoteversion); // 创建根节点
                }
                foreach ($scoperesp->auth_org_scopes->authed_dept as &$departid) {
                    $this->clidepart($c, $access_token, $departid, $remoteversion);
                }
            }
            // 根据授权用户同步
            if (isset($scoperesp->auth_org_scopes->authed_user)) {
                foreach ($scoperesp->auth_org_scopes->authed_user as &$userid) {
                    $this->cliuserByUserid($c, $access_token, $userid, $remoteversion);
                }
            }
        }
        unset($scoperesp);
        unset($c);
        $this->cleardduser($remoteversion);
        $this->cleardddepart($remoteversion);
        if ($this->isdduseropendepart()) {
            $this->syncCsmDepart();
        }
        if ($this->isdduseropenadmin()) {
            $this->syncFaAdmin();
        }
        if ($this->isdduseropendepart()) {

            $this->syncUser2Depart();
        }

        static::println('执行结束 用时:' . (time() - $start_int_time));
    }
    
    /**
     * 处理数据
     * @param $param
     * @param $resp
     * @param $fieldname
     * @return boolean
     */
    private static function doData(&$param, $resp, $fieldname)
    {
        if (is_string($fieldname)) {
            $fieldname = [$fieldname];
        }
        foreach ($fieldname as $key => $name) {
            if (is_numeric($key)) $paramfieldname = strtolower($name);
            else $paramfieldname = $key;
            $value = null;
            if (isset($resp->$name)) $value = $resp->$name;
            if (is_bool($value)) $value = $value ? 'true' : 'false';
            $param[$paramfieldname] = $value;
        }
    }
    
    // 钉钉通讯录同步开通管理员
    private function isdduseropenadmin()
    {
        $config = get_addon_config("csmding");
        $isdduseropenadmin = $config["isdduseropenadmin"];
        if ($isdduseropenadmin == 'Y') {
            return true;
        } else {
            return false;
        }
    }
    
    // 钉钉通讯录同步到本地组织架构(需要安装插件[后台管理账号增强])
    private function isdduseropendepart()
    {
        $plugin = get_addon_info('csmadmin');
        if ($plugin && $plugin['state']) {
            $config = get_addon_config("csmding");
            $isdduseropenadmin = $config["isdduseropendepart"];
            if ($isdduseropenadmin == 'Y') {
                
                return true;
            } else {
                return false;
            }
        } else {
            // 没有安装插件,则不同步
            return false;
        }
    }
    
    private function syncUser2Depart()
    {
        $dddao = new \app\admin\model\csmding\Dduser2depart();
        $csmadmindao = new \app\admin\model\csmadmin\Depart2user();
        if (true) {
            $ll = $csmadmindao->alias('csmadmin')
            ->join('csmadmin_depart depart', "csmadmin.csmadmin_depart_id=depart.id AND depart.`status`='normal'")
            ->join('admin admin', "csmadmin.faadmin_id=admin.id AND admin.`status`='normal'")
            ->join('csmding_dduser2depart ding', "csmadmin.faadmin_id=admin.id and csmadmin.csmadmin_depart_id=depart.id", 'left')
            ->where("csmadmin.status='normal'")
            ->where("ding.id is null")
            ->field('csmadmin.id')
            ->select();
            $ids = [];
            foreach ($ll as $item) {
                $ids[] = $item->id;
            }
            $csmadmindao->where('id', 'in', $ids)->delete();
            
            $ll = $dddao->alias('ding')
            ->join('csmding_dddepartment dddepart', "ding.csmding_dddepartment_id=dddepart.id  AND dddepart.`status`='normal'")
            ->join('csmding_dduser dduser', "ding.csmding_dduser_id=dduser.id AND dduser.`status`='normal'")
            ->join('csmadmin_depart2user csmadmin', "csmadmin.faadmin_id=dduser.faadmin_id and csmadmin.csmadmin_depart_id=dddepart.csmadmin_depart_id and csmadmin.status='normal'", 'left')
            ->where("csmadmin.id is null")
            ->field('dduser.faadmin_id,dddepart.csmadmin_depart_id')
            ->select();
            foreach ($ll as $item) {
                $csmadmindao->create([
                    'csmadmin_depart_id' => $item->csmadmin_depart_id,
                    'faadmin_id' => $item->faadmin_id
                ]);
            }
        }
    }
    
    // 同步开通Fadmin
    private function syncFaAdmin()
    {
        $dddao = new \app\admin\model\csmding\Dduser();
        $ddpart = new \app\admin\model\csmding\Dddepartment();

        $dao = new \app\admin\model\Admin();
        $user = new \app\admin\model\User();
        if (true) {
            // 禁用帐号
            $ll = $dddao->where('status', '=', 'hidden')->select();
            $faadminids = [];
            foreach ($ll as $item) {
                $faadminids[] = $item->faadmin_id;
            }
            $dao->where("id", 'in', $faadminids)->update([
                'status' => 'hidden',
                'updatetime' => time()
            ]);
        }
        
        // 同步新增或修改帐号
        if (true) {
            $ll = $dddao->alias('t')
            ->join('admin a', "t.faadmin_id=a.id and a.status='normal'", 'left')
            ->where('t.status', '=', 'normal')
            ->field('a.id a_id,t.userid ,t.name,t.email,t.faadmin_id,t.id')
            ->select();
            foreach ($ll as $item) {
                if ($item->a_id != null) {
                    $faadminid = $item->faadmin_id;
                    $dao->where('id', '=', $faadminid)->update([
                        'nickname' => $item['name'],
                        'email' => $item['email'],
                        'updatetime' => time()
                    ]);
                } else {
                    //$username = substr($item['userid'], - 20);
                    $dduser2departdao = new \app\admin\model\csmding\Dduser2depart();
                    $depart_id = $dduser2departdao->where('csmding_dduser_id',$item->id)->column('csmding_dddepartment_id');
                    $depart_arr = $ddpart->whereIn('id',$depart_id)->column('name');
                    $depart = '';
                    foreach($depart_arr as $v){
                        $depart .= $v.' ';
                    }
                    // dump($depart);
                    // $business_rule =  config('site.business_rule');
                    // $web_rule =  config('site.web_rule');
                    // $web_rule = explode(',',$web_rule);
                    // $business_rule = explode(',',$business_rule);
                    // $product_rule =  config('site.product_rule');
                    // $product_rule = explode(',',$product_rule);
                    if(0){
                        // if(in_array($group_id,$web_rule)){
                        // $user_params = [
                        //     'username' => time() . Random::alpha(4),
                        //     'nickname' => $item['name'],
                        //     'salt' => Random::alnum(),
                        //     'password' => time(),
                        //     'email' => $item['email'],
                        //     'avatar' => '/assets/img/avatar.png',
                        //     'createtime' => time()
                        // ];
                        // $result = $user->create($user_params);
                        // $dddao->where('id', '=', $item->id)->update([
                        //     'fauser_id' => $result->id,
                        //     'updatetime' => time()
                        // ]);
                    }else{
                        $user_params = [
                            'username' => time() . Random::alpha(4),
                            'nickname' => $item['name'],
                            'depart'=>$depart,
                            'salt' => Random::alnum(),
                            'password' => time(),
                            'email' => $item['email'],
                            'avatar' => '/assets/img/avatar.png',
                            'createtime' => time()
                        ];
                        $result_user = $user->create($user_params);
                    $params = [
                        'username' => time() . Random::alpha(4),
                        'nickname' => $item['name'],
                        'salt' => Random::alnum(),
                        'password' => time(),
                        'email' => $item['email'],
                        'loginfailure' => 0,
                        'avatar' => '/assets/img/avatar.png',
                        'createtime' => time()
                    ];
                    // var_dump($params);
                    $result = $dao->create($params);
                    // 回写dd表
                    $adminid = $result->id;
                    $user_id = $result_user->id;
                    $acc_id = 0;
                    // if(in_array($group_id,$business_rule)){
                    //     $acc_id = 8;
                    // }
                    // if(in_array($group_id,$product_rule)){
                    //     $acc_id = 7;
                    // }
                   
                    $dddao->where('id', '=', $item->id)->update([
                        'faadmin_id' => $adminid,
                        'fauser_id'=>$user_id,
                        'updatetime' => time()
                    ]);
                    
                    // 默认角色
                    $config = get_addon_config("csmding");
                    $ddadmindefaultroles = $config['ddadmindefaultroles'];
                    // FA 模式
                    if ($ddadmindefaultroles != null && $ddadmindefaultroles != '') {
                        $ddadmindefaultrolesarr = explode(",", $ddadmindefaultroles);
                        // 按照fa的授权模式
                        if($acc_id){
                            $dataset[] = [
                                'uid' => $adminid,
                                'group_id' => $acc_id
                            ];
                        }else{
                            $dataset = [];
                            foreach ($ddadmindefaultrolesarr as $value) {
                                $dataset[] = [
                                    'uid' => $adminid,
                                    'group_id' => $value
                                ];
                            }
                        }
                       
                        $groupaccessdao = new \app\admin\model\AuthGroupAccess();
                        $groupaccessdao->saveAll($dataset);
                    }
                    // csmadmin 模式
                    $plugin = get_addon_info('csmadmin');
                    if ($plugin && $plugin['state']) {
                        if ($ddadmindefaultroles != null && $ddadmindefaultroles != '') {
                            $ddadmindefaultrolesarr = explode(",", $ddadmindefaultroles);
                            
                            $dataset = [];
                            foreach ($ddadmindefaultrolesarr as $value) {
                                $dataset[] = [
                                    'faadmin_id' => $adminid,
                                    'auth_group_id' => $value
                                ];
                            }
                            $group2admindao = new \app\admin\model\csmadmin\Group2admin();
                            $group2admindao->saveAll($dataset);
                        }
                    }
                }
            }
            }
        }
    }
    
    // 同步开通csmadmin部门
    private function syncCsmDepart()
    {
        $dddao = new \app\admin\model\csmding\Dddepartment();
        $dao = new \app\admin\model\csmadmin\Depart();
        
        // 同步删除部门(to csmadmin_depart)
        if (true) {
            $ll = $dddao->where('status', '=', 'hidden')->select();
            $csmadmin_departids = [];
            foreach ($ll as $item) {
                $csmadmin_departids[] = $item->csmadmin_depart_id;
            }
            $dao->where("id", 'in', $csmadmin_departids)->delete();
        }
        
        // 同步部门(to csmadmin_depart)
        if (true) {
            $ll = $dddao->alias('t')
            ->join('csmadmin_depart a', "t.csmadmin_depart_id=a.id and a.status='normal'", 'left')
            ->where('t.status', '=', 'normal')
            ->field('t.csmadmin_depart_id,t.name,t.order,t.id,a.id a_id')
            ->select();
            
            foreach ($ll as $item) {
                if ($item->a_id != null) {
                    $dao->where('id', '=', $item->csmadmin_depart_id)->update([
                        'name' => $item['name'],
                        'weigh' => $item['order'],
                        'updatetime' => time()
                    ]);
                    static::println("更新到csmadmin depart:" . $item['name']);
                } else {
                    $params = [
                        'name' => $item['name'],
                        'weigh' => $item['order'],
                        'root_id' => 1,
                        'fromsys' => 'dd',
                        'fromuuid' => $item['id'],
                        'createtime' => time()
                    ];
                    $result = $dao->create($params);
                    $csmadmin_depart_id = $result->id;
                    static::println("新增到csmadmin depart:" . $item['name']);
                    
                    // 将部门id回写dd的部门表
                    $dddao->where('id', '=', $item->id)->update([
                        'csmadmin_depart_id' => $csmadmin_depart_id,
                        'updatetime' => time()
                    ]);
                }
            }
        }
        
        // 更新父节点
        $ll = $dddao->alias('t')
        ->join('csmding_dddepartment a', 't.parentid=a.id', 'left')
        ->field('t.csmadmin_depart_id,a.csmadmin_depart_id parent_csmadmin_depart_id')
        ->where('t.status', '=', 'normal')
        ->select();
        
        foreach ($ll as $item) {
            $root_id = null;
            $parent_id = null;
            // 如果没有父节点,则当前节点为根节点;如果有父节点,则root为父节点的根节点
            if ($item->parent_csmadmin_depart_id == null) {
                $parent_id = 0;
                $root_id = $item->csmadmin_depart_id;
            } else {
                $parent_id = $item->parent_csmadmin_depart_id;
                $row = $dao->where('id', '=', $item->parent_csmadmin_depart_id)
                ->where('status', '=', 'normal')
                ->find();
                if ($row) {
                    $root_id = $row->root_id;
                }
            }
            $dao->where('id', '=', $item->csmadmin_depart_id)->update([
                'parent_id' => $parent_id,
                'root_id' => $root_id,
                'updatetime' => time()
            ]);
        }
        $ll = $dao->where('status', '=', 'normal')->select();
        $this->setArrRootid($ll);
        foreach ($ll as &$item) {
            $dao->where('id', '=', $item->id)->update([
                'root_id' => $item->root_id
            ]);
        }
    }
    
    /**
     * 遍历,计算父节点
     * array(object->id,parent_id,root_id)
     */
    private function setArrRootid(&$arr)
    {
        $arr2 = [];
        foreach ($arr as &$item) {
            $arr2['I' . $item->id] = $item;
        }
        while (true) {
            $hasnullroot = false;
            foreach ($arr as &$item) {
                if ($item->root_id == null) {
                    $hasnullroot = true;
                    $parentnode = $arr2['I' . $item->parent_id];
                    $parent_id = $parentnode['parent_id'];
                    $root_id = $parentnode['root_id'];
                    $item->parent_id = $parent_id;
                    if ($root_id != null) {
                        $item->root_id = $root_id;
                    }
                }
            }
            if ($hasnullroot === false) {
                break;
            }
        }
    }
    
    /**
     * 删除同步的员工
     * @param int $remoteversion
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    private function cleardduser($remoteversion = 0)
    {
        $dao = new \app\admin\model\csmding\Dduser();
        // 同步删除任职关系
        if (true) {
            $ids = $dao->where('remoteversion', '<>', $remoteversion)
            ->where('status', '=', 'normal')
            ->column('id');
            if ($ids && count($ids) > 0) {
                $dduser2departdao = new \app\admin\model\csmding\Dduser2depart();
                $dduser2departdao->whereIn('csmding_dduser_id', $ids)->delete();
                static::println("删除任职关系:" . implode($ids, ','));
                $dao->where('status', '=', 'normal')
                ->whereIn('id', $ids)->update([
                    'status' => 'hidden',
                    'updatetime' => time()
                ]);
                static::println("删除钉钉员工:" . implode($ids, ','));
            }
        }
    }
    
    /**
     * 删除同步的部门
     * @param int $remoteversion
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    private function cleardddepart($remoteversion = 0)
    {
        $dao = new \app\admin\model\csmding\Dddepartment();
        
        $ids = $dao->where('remoteversion', '<>', $remoteversion)
        ->where('status', '=', 'normal')
        ->column('id');
        if ($ids && count($ids) > 0) {
            $dao->where('status', '=', 'normal')
            ->whereIn('id', $ids)
            ->update([
                'status' => 'hidden',
                'updatetime' => time()
            ]);
            static::println("删除钉钉部门:" . implode($ids));
        }
    }
    
    /**
     * @param DingTalkClient $c
     * @param string $access_token
     * @return \addons\csmding\library\dingtalk\ResultSet|mixed|\SimpleXMLElement
     */
    private function cliauthScope($c, $access_token)
    {
        $req = new OapiAuthScopesRequest();
        $resp = $c->execute($req, $access_token, "https://oapi.dingtalk.com/auth/scopes");
        if (empty($resp)) {
            return (object)['errcode' => 1, 'errmsg' => '获取授权信息为空'];
        }
        return $resp;
    }
    
    /**
     * @param DingTalkClient $c
     * @param string $access_token
     * @param int $userid
     * @param int $remoteversion
     * @throws \Exception
     */
    private function cliuserByUserid($c, $access_token, $userid, $remoteversion = 0)
    {
        $req = new OapiUserGetRequest();
        //$req->setUserid("040655446571753427");
        $req->setUserid($userid);
        $resp = $c->execute($req, $access_token, "https://oapi.dingtalk.com/user/get");
        if ($resp != null) {
            $dao = new \app\admin\model\csmding\Dduser();
            $dao2 = new \app\admin\model\csmding\Dduser2depart();
            $dddepart = new \app\admin\model\csmding\Dddepartment();

            //1.0.10 修复了钉钉通讯录部分员工授权造成员工重复创建的问题
            $rr = $dao->where('userid','=',$userid)->find();
            $user_id = $rr?$rr->id:0;
            
            $resp->department = [1];//部门默认挂在跟节点
            $this->_asynuser($resp, $dao, $dao2, $dddepart, $remoteversion,$user_id);
        }
        
    }
    
    /**
     * cli从钉钉同步员工到本地
     * @param DingTalkClient $c
     * @param string $access_token
     * @param int $dddepartid
     * @param int $offset
     * @param int $remoteversion
     * @throws \Exception
     */
    private function cliuser($c, $access_token, $dddepartid, $offset = 0, $remoteversion = 0)
    {
        $req = new OapiUserListbypageRequest();
        $req->setDepartmentId($dddepartid);
        $req->setOffset($offset);
        $req->setSize($this->maxQueryDepartUserCount);
        $resp = $c->execute($req, $access_token, "https://oapi.dingtalk.com/user/listbypage");
        if ($resp == null || !isset($resp->errcode) || $resp->errcode != 0) {
            self::println("部门[{$dddepartid}:{$offset}]获取失败：" . (empty($resp) || !isset($resp->errcode) ? '获取用户列表为空' : $resp->errmsg));
            return;
        }
        $dao = new \app\admin\model\csmding\Dduser();
        $dao2 = new \app\admin\model\csmding\Dduser2depart();
        $dddepart = new \app\admin\model\csmding\Dddepartment();
        //dump($resp->userlist);
        $userids = array_column($resp->userlist, 'userid');
        $userids_ids = $dao->whereIn('userid', $userids)->column('id', 'userid');
        
        foreach ($resp->userlist as &$item) {
            $this->_asynuser($item, $dao, $dao2, $dddepart, $remoteversion, isset($userids_ids[$item->userid]) ? $userids_ids[$item->userid] : 0);
        }
        unset($userids_ids);
        unset($userids);
        unset($req);
        unset($dao);
        unset($dao2);
        unset($dddepart);
        if ($resp->hasMore != null && $resp->hasMore === true) {
            $this->cliuser($c, $access_token, $dddepartid, $offset + $this->maxQueryDepartUserCount, $remoteversion);
        }
        unset($resp);
    }
    
    /**
     * 同步用户数据
     * @param $item
     * @param \app\admin\model\csmding\Dduser $dao
     * @param \app\admin\model\csmding\Dduser2depart $dao2
     * @param \app\admin\model\csmding\Dddepartment $dddepart
     * @param int $remoteversion
     * @param int $userid
     * @throws \Exception
     */
    private function _asynuser($item, $dao, $dao2, $dddepart, $remoteversion = 0, $userid = 0)
    {
        //         $dao = new \app\admin\model\csmding\Dduser();
        //         $dao2 = new \app\admin\model\csmding\Dduser2depart();
        //         $dddepart = new \app\admin\model\csmding\Dddepartment();
        
        $param = ['remoteversion' => $remoteversion, 'status' => 'normal'];
        static::doData($param, $item, [
            'userid', 'unionid', 'mobile', 'tel', 'workPlace', 'remark',
            'order', 'isAdmin', 'isBoss', 'isLeader', 'name', 'active',
            'position', 'email', 'avatar', 'jobnumber', 'orgEmail',
        ]);
        if (!isset($param['email']) || $param['email'] == null || $param['email'] == '') {
            $param['email'] = $param['userid'];
        }
        if (!isset($param['orgemail']) || $param['orgemail'] == null || $param['orgemail'] == '') {
            $param['orgemail'] = $param['email'];
        }
        // 更新部门清单
        if (!empty($userid)) {
            $param['updatetime'] = time();
            $dao->where("id", "=", $userid)->update($param);
            static::println("更新员工:" . $item->userid);
        } else {
            $param['createtime'] = time();
            $dao->create($param);
            static::println("创建员工:" . $item->userid);
            $userid = $dao->getLastInsID();
        }
        
        // 处理人员任职部门关系
        if (true) {
            $departments = $item->department;
            $user2departs = $dao2->alias('a')->where('csmding_dduser_id', '=', $userid)
            ->join('csmding_dddepartment b', 'a.csmding_dddepartment_id = b.id')
            ->column('b.id', 'b.departmentid');
            $dbuser2departs = array_keys($user2departs);
            // 删除权限组
            if (true) {
                $departids = array_unique(array_diff($departments, $dbuser2departs));
                $del_ids = [];
                if (!empty($departids)) {
                    foreach ($departids as $value) {
                        if (isset($user2departs[$value])) $del_ids[] = $user2departs[$value];
                    }
                    if (count($del_ids) > 0) {
                        $dao2->where("csmding_dduser_id", '=', $userid)
                        ->whereIn('csmding_dddepartment_id', $del_ids)->delete();
                        static::println("删除权限组:userid-{$userid}/depart[" . implode(',', $del_ids) . "]");
                    }
                }
                unset($del_ids);
                unset($departids);
            }
            unset($user2departs);
            // 新增权限组
            if (true) {
                $add_ids = array_unique(array_diff($departments, $dbuser2departs));
                $now_ids = $dddepart->whereIn('departmentid', $add_ids)->column('id');
                $param = [];
                foreach ($now_ids as &$id) {
                    $param[] = [
                        'csmding_dduser_id' => $userid,
                        'csmding_dddepartment_id' => $id
                    ];
                }
                if (count($param) > 0) {
                    $dao2->insertAll($param, false);
                    static::println("创建权限组:userid-{$userid}/depart[" . implode(',', $now_ids) . "]");
                }
                unset($add_ids);
                unset($now_ids);
            }
            unset($departments);
            unset($dbuser2departs);
        }
        unset($row);
        unset($param);
        unset($userid);
    }
    
    //部分授权会存在没有跟节点,为了显示树形结构,需要人工补全跟节点
    private function _asynrootdepart($remoteversion)
    {
        $rootdepartid = 1;
        $dao = new \app\admin\model\csmding\Dddepartment();
        $id = $dao->where('departmentid', '=', $rootdepartid)->value('id');
        if ($id) {
            $dao->where('id', '=', $id)->update([
                'remoteversion' => $remoteversion,
                'status' => 'normal',
                'updatetime' => time(),
            ]);
            return $id;
        }
        return $dao->insertGetId([
            'departmentid' => $rootdepartid,
            'name' => '根节点',
            'parentid' => 0,
            'order' => '0',
            'status' => 'normal',
            'createtime' => time(),
            'remoteversion' => $remoteversion,
        ]);
    }
    
    /**
     * cli从钉钉同步部门到本地
     * @param DingTalkClient $c
     * @param string $access_token
     * @param int $departid
     * @param int $remoteversion
     * @throws \Exception
     */
    private function clidepart($c, $access_token, $departid, $remoteversion = 0)
    {
        
        $req = new OapiDepartmentListRequest();
        $req->setId($departid);
        $resp = $c->execute($req, $access_token, "https://oapi.dingtalk.com/department/list");
        //var_dump($resp);
        if ($resp == null || !isset($resp->errcode) || $resp->errcode != '0') {
            self::println("部门[{$departid}]获取失败：" . (empty($resp) || !isset($resp->errcode) ? '获取列表数据为空' : $resp->errmsg));
            return;
        }
        //部门按照副组织排序
        $resp->department = $this->_sortdepartbylevel($resp->department);
        
        $reqd = new OapiDepartmentGetRequest();
        $dao = new \app\admin\model\csmding\Dddepartment();
        $departids = array_column($resp->department, 'id');
        $departids[] = $departid;
        $departids_ids = $dao->whereIn('departmentid', $departids)->column('id', 'departmentid');
        //由于当前跟节点在list中不返回,需要额外调用
        $this->_asyndepart($c, $reqd, $access_token, $departid, $dao, $remoteversion, isset($departids_ids[$departid]) ? $departids_ids[$departid] : 0);
        foreach ($resp->department as &$item) {
            $this->_asyndepart($c, $reqd, $access_token, $item->id, $dao, $remoteversion, isset($departids_ids[$item->id]) ? $departids_ids[$item->id] : 0);
        }
        unset($departids_ids);
        unset($departids);
        unset($req);
        unset($resp);
        unset($reqd);
        unset($dao);
    }
    
    //由于部门写入数据库,先要写父节点再写子节点
    //需要对钉钉的部门数按照层数排序
    //排序逻辑:$sr为排序后的结果,依次将找不到父节点的节点先排序
    //v1.0.6
    private function _sortdepartbylevel($departs){
        $sr = [];
        $index = 0;
        while(count($departs)>0 && $index<10000){
            $index++;
            foreach($departs as $i=>$item){
                $hasparent = false;
                foreach($departs as $iitem){
                    if($item->parentid==$iitem->id){
                        $hasparent = true;
                    }
                }
                if($hasparent===false){
                    $sr[] = $item;
                    //echo '[remove-'.$i.']';
                    unset($departs[$i]);
                }
            }
        }
        return $sr;
    }
    
    /**
     * 同步部门数据
     * @param DingTalkClient $c
     * @param OapiDepartmentGetRequest $reqd
     * @param string $access_token
     * @param int $departtid
     * @param \app\admin\model\csmding\Dddepartment $dao
     * @param int $remoteversion
     * @param int $departmentid
     * @throws \Exception
     */
    private function _asyndepart($c, $reqd, $access_token, $departtid, $dao, $remoteversion = 0, $departmentid = 0)
    {
        
        //$row = $dao->where('departmentid', '=', $departtid)->find();
        $reqd->setId($departtid);
        $respd = $c->execute($reqd, $access_token, "https://oapi.dingtalk.com/department/get");
        if ($respd == null || !isset($respd->errcode) || $respd->errcode != 0) {
            self::println("部门[{$departtid}]获取失败：" . (empty($respd) || !isset($respd->errcode)  ? '获取部门数据为空' : $respd->errmsg));
            return;
        }
        $param = ['remoteversion' => $remoteversion, 'status' => 'normal'];
        
        static::doData($param, $respd, [
            'name', 'ext', 'userPermits', 'userPerimits', 'orgDeptOwner', 'outerDept',
            'deptManagerUseridList', 'groupContainSubDept', 'outerPermitUsers', 'outerPermitDepts',
            'deptPerimits', 'createDeptGroup', 'deptGroupChatId', 'departmentid' => 'id', 'autoAddUser',
            'autoAddUser', 'deptHiding', 'deptPermits', 'order', 'parentid'
        ]);
        
        // 处理父节点
        if (isset($param['parentid'])) {
            $rowparent = $dao->where('departmentid', '=', $param['parentid'])->find();
            if ($rowparent != null) {
                $param['parentid'] = $rowparent->id;
            }
            unset($rowparent);
        }
        $param['parentid'] = ($param['parentid']==null)?0:$param['parentid'];
        
        // $csmding_dddepartment_id = null;
        // 更新部门清单
        if (!empty($departmentid)) {
            $param['updatetime'] = time();
            $dao->where("id", "=", $departmentid)->update($param);
            static::println("更新部门: " . $respd->name);
        } else {
            $param['createtime'] = time();
            static::println("创建部门: " . $respd->name);
 
            $dao->create($param);
            //$departmentid = $dao->getLastInsID();
        }
        unset($param);
        unset($row);
        unset($respd);
        $this->cliuser($c, $access_token, $departtid, 0, $remoteversion);
    }
    
    public static function println($str)
    {
        echo $str . "<BR>\n";
        //echo str_repeat(" ", 4096);
        echo ob_get_clean();    //获取当前缓冲区内容并清除当前的输出缓冲
        flush();   //刷新缓冲区的内容，输出
    }
}
